package com.starview.star.operators.io.runtime;

import com.starview.platform.data.EvaluationException;
import com.starview.platform.data.IFunction;
import com.starview.platform.data.IValue;
import com.starview.platform.data.type.IType;
import com.starview.platform.data.type.StandardTypes;
import com.starview.platform.data.type.TypeUtils;
import com.starview.platform.data.value.Factory;
import com.starview.platform.data.value.ResourceURI;
import com.starview.platform.resource.ResourceException;
import com.starview.platform.resource.Resources;
import com.starview.star.compiler.ErrorReport;
import com.starview.star.compiler.ast.IAbstract;
import com.starview.star.compiler.grammar.OpGrammar;
import com.starview.star.compiler.operator.Operators;
import com.starview.star.operators.CafeEnter;

import java.io.IOException;
import java.io.Reader;

public class BasicIo{
  public static class ReadLn implements IFunction{
    public static final String name = "__readLn";

    @CafeEnter
    public static String enter(IOHandle hdl) throws EvaluationException{
      return hdl.readLn();
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return Factory.newString(enter((IOHandle) args[0]));
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.functionType(IOHandle.type, StandardTypes.rawStringType);
    }
  }

  public static class ReadChar implements IFunction{
    public static final String name = "__readChar";

    @CafeEnter
    public static int enter(IOHandle hdl) throws EvaluationException{
      return hdl.readChar();
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return Factory.newChar(enter((IOHandle) args[0]));
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.functionType(IOHandle.type, StandardTypes.rawCharType);
    }
  }

  public static class ReadAll implements IFunction{
    public static final String name = "__readAll";

    @CafeEnter
    public static String enter(IOHandle hdl) throws EvaluationException{
      StringBuilder blder = new StringBuilder();
      do {
        String line = hdl.readLn();
        if (line != null)
          blder.append(line);
        else
          return blder.toString();
      } while (true);
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return Factory.newString(enter((IOHandle) args[0]));
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.functionType(IOHandle.type, StandardTypes.rawStringType);
    }
  }

  public static class ReadQuoted implements IFunction{
    public static final String name = "__read_quoted";

    @CafeEnter
    public static IAbstract enter(ResourceURI uri) throws EvaluationException{
      ErrorReport errors = new ErrorReport();
      Operators operators = Operators.operatorRoot().copy();
      OpGrammar parser = new OpGrammar(operators, errors);

      try {
        try (Reader rdr = Resources.getReader(uri)) {
          IAbstract term = parser.parse(uri, rdr, null);
          if (errors.isErrorFree())
            return term;
          else throw new EvaluationException(errors.toString());
        }
      } catch (ResourceException e) {
        throw new EvaluationException("problem in accessing resource " + uri + ": " + e.getMessage());
      } catch (IOException e) {
        throw new EvaluationException("IO problem in accessing resource " + uri + ": " + e.getMessage());
      }
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return enter((ResourceURI) args[0]);
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.functionType(ResourceURI.type, StandardTypes.astType);
    }
  }

  public static class AtEof implements IFunction{
    public static final String name = "__atEof";

    @CafeEnter
    public static boolean enter(IOHandle hdl) throws EvaluationException{
      return hdl.atEof();
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return Factory.newBool(enter((IOHandle) args[0]));
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.functionType(IOHandle.type, StandardTypes.booleanType);
    }
  }

  public static class WriteLn implements IFunction{
    public static final String name = "__writeLn";

    @CafeEnter
    public static IValue enter(IOHandle hdl, String line) throws EvaluationException{
      try {
        hdl.getOutput().write(line);
        hdl.getOutput().write('\n');
      } catch (IOException e) {
        throw new EvaluationException("problem in writing to " + hdl, e);
      }
      return StandardTypes.unit;
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return enter((IOHandle) args[0], Factory.stringValue(args[1]));
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.procedureType(IOHandle.type, StandardTypes.rawStringType);
    }
  }

  public static class WriteStr implements IFunction{
    public static final String name = "__writeStr";

    @CafeEnter
    public static IValue enter(IOHandle hdl, String line) throws EvaluationException{
      try {
        hdl.getOutput().write(line);
      } catch (IOException e) {
        throw new EvaluationException("problem in writing to " + hdl, e);
      }
      return StandardTypes.unit;
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException{
      return enter((IOHandle) args[0], Factory.stringValue(args[1]));
    }

    @Override
    public IType getType(){
      return type();
    }

    public static IType type(){
      return TypeUtils.procedureType(IOHandle.type, StandardTypes.rawStringType);
    }
  }
}
